/*
 * Copyright (c) 2025 Li Auto Inc. and its affiliates
 * Licensed under the Apache License, Version 2.0(the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <string.h>
#include "nvm_fsm.h"

#define NVM_START_SEC_PRIVATE_BSS
#include "nvm_memmap.h"

VAR(nvm_block_info_type, NVM_PRIVATE_BSS) nvm_current_block_info;
VAR(nvm_job_t, NVM_PRIVATE_BSS) nvm_current_job;
VAR(nvm_fsm_state_type, NVM_PRIVATE_BSS) nvm_task_state;
VAR(nvm_fsm_state_type, NVM_PRIVATE_BSS) nvm_subtask_state;

#define NVM_STOP_SEC_PRIVATE_BSS
#include "nvm_memmap.h"

#define NVM_START_SEC_CONST
#include "nvm_memmap.h"
CONST(nvm_service_id_t, NVM_CONST)
nvm_service_table[NVM_INT_FID_INVALID_JOB + 1u] = {
	NVM_WRITE_BLOCK, NVM_READ_BLOCK, NVM_RESTORE_BLOCK_DEFAULTS, NVM_INVALIDATE_NV_BLOCK, NVM_ERASE_BLOCK,
	NVM_WRITE_ALL,	 NVM_READ_ALL,	 NVM_MAINFUNCTION,	     NVM_CANCEL_JOBS
};

static CONST(nvm_state_descr_type, NVM_CONST) nvm_state_descr_table_at[] = {
	/* NVM_FSM_UNINIT */
	{ /* Exit 1 */
	  { NVM_EVENT_TRUE, NVM_ACT_Wait, NVM_FSM_UNINIT },
	  /* Exit 2 */
	  { NVM_ACT_Wait, NVM_FSM_UNINIT } },
	/* NVM_FSM_IDLE */
	{ /* Found Job */
	  { NVM_EVENT_HAVE_JOB, NVM_ACT_Nop, NVM_FSM_HAVE_JOB },
	  /* No Job */
	  { NVM_ACT_Wait, NVM_FSM_IDLE } },
	/* NVM_FSM_HAVE_JOB */
	{ /* Found High Priority Job */
	  { NVM_EVENT_HAVE_HIGHPRIO_JOB, NVM_ACT_GetHighPrioJob, NVM_FSM_HIGH_PRIO_JOB },
	  /* Normal Priority Job or MultiBlock Job */
	  { NVM_ACT_Nop, NVM_FSM_NOTHIGHPRIO_JOB } },
	/* NVM_FSM_HIGH_PRIO_JOB */
	{ /* Sub Task Still in Running */
	  { NVM_EVENT_SUBTASK_RUNNING, NVM_ACT_Nop, NVM_FSM_HIGH_PRIO_JOB },
	  /* Job Finished */
	  { NVM_ACT_FinishBlock, NVM_FSM_IDLE } },
	/* NVM_FSM_NOTHIGHPRIO_JOB */
	{ /* Found Normal Priority Job */
	  { NVM_EVENT_NORMAL_PRIO_JOB, NVM_ACT_GetNormalPrioJob, NVM_FSM_NORMAL_PRIO_JOB_1 },
	  /* Found MultiBlock Job */
	  { NVM_ACT_Nop, NVM_FSM_MULTI_BLOCK_JOB } },
	/* NVM_FSM_NORMAL_PRIO_JOB_1 */
	{ /* Immediate Job Occurred */
	  { NVM_EVENT_HAVE_HIGHPRIO_JOB, NVM_ACT_GetHighPrioJob, NVM_FSM_HIGH_PRIO_JOB },
	  /* Continue Normal Prio Job */
	  { NVM_ACT_Nop, NVM_FSM_NORMAL_PRIO_JOB_2 } },
	/* NVM_FSM_NORMAL_PRIO_JOB_2 */
	{ /* Continue Running job */
	  { NVM_EVENT_SUBTASK_RUNNING, NVM_ACT_Nop, NVM_FSM_NORMAL_PRIO_JOB_1 },
	  /* Job Finished */
	  { NVM_ACT_FinishBlock, NVM_FSM_IDLE } },
	/* NVM_FSM_MULTI_BLOCK_JOB */
	{ /* Found WriteAll Job */
	  { NVM_EVENT_HAVEWRITEALL_JOB, NVM_ACT_StartWriteAll, NVM_FSM_WRITEALL_PROC_1 },
	  /* Either WriteAll or ReadAll */
	  { NVM_ACT_StartReadAll, NVM_FSM_READALL_PROC_1 } },
	/* NVM_FSM_READ_INIT */
	{ /* NV busy, just Wait */
	  { NVM_EVENT_NV_BUSY, NVM_ACT_Wait, NVM_FSM_READ_INIT },
	  /* Not busy, Read NV Block */
	  { NVM_ACT_ReadNvBlock, NVM_FSM_READ_READ_DATA } },
	/* NVM_FSM_READ_READ_DATA */
	{ /* NV busy, just Wait */
	  { NVM_EVENT_NV_BUSY, NVM_ACT_Wait, NVM_FSM_READ_READ_DATA },
	  /* NV Idle and Read Fininshed */
	  { NVM_ACT_Nop, NVM_FSM_READ_NV_FINISH } },
	/* NVM_FSM_READ_NV_FINISH */
	{ /* Read OK, Go to Compare CRC */
	  { NVM_EVENT_LAST_RESULT_OK, NVM_ACT_Nop, NVM_FSM_READ_CMP_CRC },
	  /* Read Not OK, Go to Recovery */
	  { NVM_ACT_Nop, NVM_FSM_READ_RETRY } },
	/* NVM_FSM_READ_CMP_CRC */
	{ /* No CRC or Compare CRC OK */
	  { NVM_EVENT_CRC_MATCH, NVM_ACT_FinishReadBlock, NVM_FSM_FSM_FINISHED },
	  /* Compare CRC not OK */
	  { NVM_ACT_Nop, NVM_FSM_READ_RETRY } },
	/* NVM_FSM_READ_RETRY */
	{ /* Need Retry Read */
	  { NVM_EVENT_READ_RETRY, NVM_ACT_AddTriedTimes, NVM_FSM_READ_INIT },
	  /* Retry Fail and Recover */
	  { NVM_ACT_Nop, NVM_FSM_READ_RECOV } },
	/* NVM_FSM_READ_RECOV */
	{ /* Redundant block && the first sub block, try the other one*/
	  { NVM_EVENT_REDUNDANT_BLOCK, NvM_ACT_SetupRedundant, NVM_FSM_READ_INIT },
	  /* Not redundant || the second sub block Fail, try to restore ROM */
	  { NVM_ACT_FinishRestoreRom, NVM_FSM_FSM_FINISHED } },
	/* NVM_FSM_WRITE_INIT */
	{ /* Create Data and Calculate CRC */
	  { NVM_EVENT_TRUE, NVM_ACT_InitWriteBlock, NVM_FSM_CRCCOMPARE },
	  /* No Use */
	  { NVM_ACT_Nop, NVM_FSM_FSM_FINISHED } },
	/* NVM_FSM_CRCCOMPARE */
	{ /*CRC Copmare True*/
	  { NVM_EVENT_CrcCompare, NVM_ACT_WriteFinshed, NVM_FSM_FSM_FINISHED },
	  /*CRC Copmare FALSE*/
	  { NVM_ACT_Nop, NVM_FSM_WRITE_DATA_1 } },
	/* NVM_FSM_WRITE_DATA_1 */
	{ /* NV Busy, Just Wait */
	  { NVM_EVENT_NV_BUSY, NVM_ACT_Wait, NVM_FSM_WRITE_DATA_1 },
	  /* NV Idle,  Write Block */
	  { NVM_ACT_WriteNvBlock, NVM_FSM_WRITE_DATA_2 } },
	/* NVM_FSM_WRITE_DATA_2 */
	{ /* NV Busy, Just Wait */
	  { NVM_EVENT_NV_BUSY, NVM_ACT_Wait, NVM_FSM_WRITE_DATA_2 },
	  /* Finish Write, Check whether need retry */
	  { NVM_ACT_Nop, NVM_FSM_WRITE_RETRY } },
	/* NVM_FSM_WRITE_RETRY */
	{ /* Check Need Write Retry */
	  { NVM_EVENT_WRITE_RETRY, NVM_ACT_AddTriedTimes, NVM_FSM_WRITE_DATA_1 },
	  /* Finish Write, Check Redundant */
	  { NVM_ACT_Nop, NVM_FSM_WRITE_REDUNDANT } },
	/* NVM_FSM_WRITE_REDUNDANT */
	{ /* Redundant, Write the other Block */
	  { NVM_EVENT_REDUNDANT_BLOCK, NvM_ACT_SetupRedundant, NVM_FSM_WRITE_DATA_1 },
	  /* Not Redundant or the other have Finished */
	  { NVM_ACT_WriteFinshed, NVM_FSM_FSM_FINISHED } },
	/* NVM_FSM_RESTORE_INIT */
	{ /* Restore Block */
	  { NVM_EVENT_TRUE, NVM_ACT_RestoreBlockDefaults, NVM_FSM_FSM_FINISHED },
	  /* No Use */
	  { NVM_ACT_Nop, NVM_FSM_FSM_FINISHED } },
	/* NVM_FSM_WRITEALL_PROC_1 */
	{ /* SubTask still Write, Do Nothing */
	  { NVM_EVENT_SUBTASK_RUNNING, NVM_ACT_Nop, NVM_FSM_WRITEALL_PROC_1 },
	  /* SubTask Finished, Write next Block */
	  { NVM_ACT_WriteNextBlock, NVM_FSM_WRITEALL_PROC_2 } },
	/* NVM_FSM_WRITEALL_PROC_2 */
	{ /* If WriteAll Canceled or Last Block Have Written, Finsh WriteAll */
	  { NVM_EVENT_WRITEALL_END, NVM_ACT_FinishWriteAll, NVM_FSM_IDLE },
	  /* Not End, Go to Step1 */
	  { NVM_ACT_Nop, NVM_FSM_WRITEALL_PROC_1 } },
	/* NVM_FSM_READALL_PROC_1 */
	{ /* SubTask still Read, Do Nothing */
	  { NVM_EVENT_SUBTASK_RUNNING, NVM_ACT_Nop, NVM_FSM_READALL_PROC_1 },
	  /* SubTask Finished, Go to Step 2 */
	  { NVM_ACT_Nop, NVM_FSM_READALL_PROC_2 } },
	/* NVM_FSM_READALL_PROC_2 */
	{ /* Last Block Have Read */
	  { NVM_EVENT_LAST_BLOCK_DONE_READALL, NVM_ACT_FinishReadAll, NVM_FSM_IDLE },
	  /* Not Last Block, Read Next */
	  { NVM_ACT_ReadNextBlock, NVM_FSM_READALL_PROC_1 } },
	/* NVM_FSM_INVALIDATE_INIT */
	{ /* NV Busy, Just Wait */
	  { NVM_EVENT_NV_BUSY, NVM_ACT_Wait, NVM_FSM_INVALIDATE_INIT },
	  /* NV Idle, Excute */
	  { NVM_ACT_InitInvalidteBlock, NVM_FSM_INVALIDATE_ING } },
	/* NVM_FSM_INVALIDATE_ING */
	{ /* NV Busy, Just Wait */
	  { NVM_EVENT_NV_BUSY, NVM_ACT_Wait, NVM_FSM_INVALIDATE_ING },
	  /* Finish, Checkt Redundant */
	  { NVM_ACT_Nop, NVM_FSM_INVALIDATE_REDUNDANT } },
	/* NVM_FSM_INVALIDATE_REDUNDANT */
	{ /* Redundant, Excute the other */
	  { NVM_EVENT_REDUNDANT_BLOCK, NvM_ACT_SetupRedundant, NVM_FSM_INVALIDATE_INIT },
	  /* Not Redundant or the other have Finished */
	  { NVM_ACT_FinishBlock, NVM_FSM_FSM_FINISHED } },
	/* NVM_FSM_ERASE_INIT */
	{ /* NV Busy, Just Wait */
	  { NVM_EVENT_NV_BUSY, NVM_ACT_Wait, NVM_FSM_ERASE_INIT },
	  /* NV Idle, Excute */
	  { NVM_ACT_EraseNvBlock, NVM_FSM_ERASE_ING } },
	/* NVM_FSM_ERASE_ING */
	{ /* NV Busy, Just Wait */
	  { NVM_EVENT_NV_BUSY, NVM_ACT_Wait, NVM_FSM_ERASE_ING },
	  /* Finish, Checkt Redundant */
	  { NVM_ACT_Nop, NVM_FSM_ERASE_REDUNDANT } },
	/* NVM_FSM_ERASE_REDUNDANT */
	{ /* Redundant, Excute the other */
	  { NVM_EVENT_REDUNDANT_BLOCK, NvM_ACT_SetupRedundant, NVM_FSM_ERASE_INIT },
	  /* Not Redundant or the other have Finished */
	  { NVM_ACT_FinishBlock, NVM_FSM_FSM_FINISHED } },
	/* NVM_FSM_FSM_FINISHED */
	{ /* Do nothing */
	  { NVM_EVENT_TRUE, NVM_ACT_Nop, NVM_FSM_FSM_FINISHED },
	  /* Do nothing */
	  { NVM_ACT_Nop, NVM_FSM_FSM_FINISHED } }
};

#define NVM_STOP_SEC_CONST
#include "nvm_memmap.h"

#define NVM_START_SEC_CODE
#include "nvm_memmap.h"
static FUNC(boolean, NVM_CODE) nvm_fsm_event(nvm_event_type nvm_queries_at)
{
	return nvm_event_table[nvm_queries_at]();
}

static FUNC(void, NVM_CODE) nvm_fsm_action(nvm_state_action_type nvm_actions_at)
{
	nvm_action_table[nvm_actions_at]();
}

FUNC(void, NVM_CODE) nvm_fsm_init(void)
{
	nvm_current_block_info.internal_flags = 0u;

	nvm_current_job.job_service_id = NVM_INT_FID_NO_JOB_PENDING;
	nvm_current_job.job_block_id = 0u;
	nvm_current_job.ram_addr = NULL;
	nvm_current_block_info.retry_times = 0u;

	nvm_task_state = NVM_FSM_IDLE;
	nvm_subtask_state = NVM_FSM_FSM_FINISHED;
}

FUNC(void, NVM_CODE) nvm_fsm_deinit(void)
{
	(void)memset(&nvm_current_job, 0, sizeof(nvm_current_job));
	(void)memset(&nvm_current_block_info, 0, sizeof(nvm_current_block_info));
	nvm_task_state = NVM_FSM_UNINIT;
	nvm_subtask_state = NVM_FSM_UNINIT;
}

FUNC(nvm_fsm_state_type, NVM_CODE) nvm_fsm_excute(nvm_fsm_state_type nvm_current_state)
{
	nvm_fsm_state_type nvm_ret_state_tloc;
	nvm_state_action_type change_actions_ptloc = NVM_ACT_Nop;
	const nvm_state_descr_type *current_state_ptloc = &nvm_state_descr_table_at[nvm_current_state];

	if (nvm_fsm_event(current_state_ptloc->change_conditions.queries)) {
		nvm_ret_state_tloc = current_state_ptloc->change_conditions.next_state;
		change_actions_ptloc = current_state_ptloc->change_conditions.actions;
	} else {
		nvm_ret_state_tloc = current_state_ptloc->final_condition.next_state;
		change_actions_ptloc = current_state_ptloc->final_condition.actions;
	}

	nvm_fsm_action(change_actions_ptloc);
	return nvm_ret_state_tloc;
}

#define NVM_STOP_SEC_CODE
#include "nvm_memmap.h"
